#include <errno.h>
#include <sys/stat.h>
#include <mysql/mysql.h>
#include <unistd.h>
#include <fcntl.h>
# include "head.h"

status_t st1;

// 保存接收的文件偏移量到文件中
void save_received_offset(const char *path, const char *file_name, off_t offset) {
    char offset_file[256];
    snprintf(offset_file, sizeof(offset_file), "%s.offset", path);
    FILE *fp = fopen(offset_file, "w");
    if (fp) {
        fprintf(fp, "%ld\n", offset);
        fclose(fp);
    }
}

// 从文件中读取已接收的文件偏移量
off_t get_received_offset(const char *path, const char *file_name) {
    char offset_file[256];
    snprintf(offset_file, sizeof(offset_file), "%s.offset", path);
    FILE *fp = fopen(offset_file, "r");
    off_t offset = 0;
    if (fp) {
        fscanf(fp, "%ld", &offset);
        fclose(fp);
    }
    return offset;
}

int query_user_id_by_pre_dir(char *path, client_status *user) {
    MYSQL *conn = user->sql;
    MYSQL_RES *result;
    MYSQL_ROW row;

    pthread_mutex_init(&st1.lock, NULL);
    pthread_mutex_lock(&st1.lock);

    char sql_query[1024];
    
    snprintf(sql_query, sizeof(sql_query),
             "SELECT id FROM files WHERE files.path = '%s' AND files.uid = '%s' AND files.file_type = '1'",
             path,user->user_id);

    if (mysql_query(conn, sql_query)) {
        fprintf(stderr, "SELECT pre_query failed: %s\n", mysql_error(conn));
        mysql_close(conn);
        pthread_mutex_unlock(&st1.lock);
        return -1;
    }

    result = mysql_store_result(conn);
    if ((row = mysql_fetch_row(result)) == NULL) {
        printf("找不到结果默认为-1\n");
        mysql_free_result(result);
        pthread_mutex_unlock(&st1.lock);
        return -1;
    }

    int pre_id = atoi(row[0]);
    mysql_free_result(result);
    pthread_mutex_unlock(&st1.lock);

    return pre_id;
}

bool is_file_in_db(int uid, const char *path, client_status *user, sendMsg msg,char *filename) {
    MYSQL *conn = user->sql;
    MYSQL_RES *result;
    pthread_mutex_init(&st1.lock, NULL);
    pthread_mutex_lock(&st1.lock);

    char sql_query[4096];
    snprintf(sql_query, sizeof(sql_query),
             "SELECT * FROM files WHERE uid = '%s' AND path = '%s' AND hash = '%s' AND finish = 1 AND file_name = '%s'",
             user->user_id, path, msg.hash_values, filename);

    if (mysql_query(conn, sql_query)) {
        fprintf(stderr, "SELECT echo query failed: '%s'\n", mysql_error(conn));
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    result = mysql_store_result(conn);
    if (mysql_num_rows(result) == 0) {
        mysql_free_result(result);
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    mysql_free_result(result);
    pthread_mutex_unlock(&st1.lock);
    return true;
}

bool is_fastputs(int uid, const char *path, client_status *user, sendMsg msg, char *cur_path, char *file_name) {
    MYSQL *conn = user->sql;
    MYSQL_RES *result;
    pthread_mutex_init(&st1.lock, NULL);
    pthread_mutex_lock(&st1.lock);

    char sql_query[4096];
    snprintf(sql_query, sizeof(sql_query),
             "SELECT * FROM files WHERE files.hash = '%s' AND files.finish = 1;",
             msg.hash_values);

    if (mysql_query(conn, sql_query)) {
        fprintf(stderr, "SELECT fast_query failed: '%s'\n", mysql_error(conn));
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    result = mysql_store_result(conn);
    if (mysql_num_rows(result) == 0) {
        mysql_free_result(result);
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    MYSQL_ROW row = mysql_fetch_row(result);
    int r3 = atoi(row[3]);
    int pre_dir=atoi(row[0]);
    char sql_insert[4096];
    snprintf(sql_insert, sizeof(sql_insert),
             "INSERT INTO files (file_name, uid, pre_dir, path, file_type, hash,finish, offset)"
             "VALUES ('%s', %s, %d, '%s', '%s', '%s', '%s', %d)",
             row[1], row[2], r3, row[4], row[5], row[6], row[8], 0);

    mysql_query(conn, sql_insert);
    mysql_free_result(result);

    snprintf(sql_query, sizeof(sql_query), "SELECT LAST_INSERT_ID()");
    if (mysql_query(conn, sql_query)) {
        fprintf(stderr, "SELECT LAST_INSERT_ID failed: %s\n", mysql_error(conn));
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    result = mysql_store_result(conn);
    row = mysql_fetch_row(result);
    if (row == NULL) {
        mysql_free_result(result);
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    int inserted_id = atoi(row[0]);
    char path1[5000]={0};
    snprintf(path1,sizeof(path1),"%s/%s",cur_path,file_name);
    mysql_free_result(result);


    char sql_update[8000];
    snprintf(sql_update, sizeof(sql_update),
             "UPDATE files SET path = '%s', file_name = '%s', uid = '%s', pre_dir = %d WHERE id = %d",
             path1, file_name, user->user_id, pre_dir, inserted_id);

    mysql_query(conn, sql_update);
    pthread_mutex_unlock(&st1.lock);

    return true;
}

bool insert_column(int uid,  char *path, client_status *user, sendMsg msg, char *cur_path, char *file_name) {
    MYSQL *conn = user->sql;
    pthread_mutex_init(&st1.lock, NULL);
    pthread_mutex_lock(&st1.lock);

    int pre_dir = query_user_id_by_pre_dir(cur_path, user);
    char suid[256]={0};

    snprintf(suid, sizeof(suid), "%d", uid);
    //printf("path:%s\n",path);
    char query[4096];
    snprintf(query, sizeof(query),
             "INSERT INTO files (file_name, uid, pre_dir, path, file_type, hash,create_time, finish, offset)"
             "VALUES ('%s', '%s', %d, '%s', 2, '%s',NOW(), '0', 0)",
             file_name, suid, pre_dir, path,msg.hash_values);

    if (mysql_query(conn, query)) {
        fprintf(stderr, "更新数据库失败: %s\n", mysql_error(conn));
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    pthread_mutex_unlock(&st1.lock);
    return true;
}

bool again_send(int uid, const char *path, client_status *user, sendMsg msg, char *file_name) {
    MYSQL *conn = user->sql;
    MYSQL_RES *result;
    pthread_mutex_init(&st1.lock, NULL);
    pthread_mutex_lock(&st1.lock);

    char query[4096];
    snprintf(query, sizeof(query),
             "SELECT * FROM files WHERE files.hash = '%s' AND files.finish = '0'",
             msg.hash_values);

    if (mysql_query(conn, query)) {
        fprintf(stderr, "更新数据库失败: %s\n", mysql_error(conn));
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    result = mysql_store_result(conn);
    if (mysql_num_rows(result) == 0) {
        mysql_free_result(result);
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    mysql_free_result(result);
    pthread_mutex_unlock(&st1.lock);
    return true;
}

bool update_finish(int uid, const char *path, client_status *user, sendMsg msg, char *file_name) {
    MYSQL *conn = user->sql;
    pthread_mutex_init(&st1.lock, NULL);
    pthread_mutex_lock(&st1.lock);

    char sql_update[4096];
    snprintf(sql_update, sizeof(sql_update),
             "UPDATE files SET finish = '1' WHERE uid = '%s' AND file_name = '%s' AND hash = '%s' AND finish = '0'",
             user->user_id, file_name, msg.hash_values);

    if (mysql_query(conn, sql_update)) {
        fprintf(stderr, "更新数据库失败: %s\n", mysql_error(conn));
        pthread_mutex_unlock(&st1.lock);
        return false;
    }

    pthread_mutex_unlock(&st1.lock);
    return true;
}

bool my_puts(sendMsg msg, client_status *user, int net_fd) {
    //recv(net_fd, &msg, sizeof(msg), 0);
    char path[8000];
    snprintf(path, sizeof(path), "%s%s%s", user->path, msg.msg,msg.file_name);
    printf("path：%s\n",path);
    char cur_path[10000]={0};
    snprintf(cur_path, sizeof(cur_path), "%s%s", user->path, msg.msg);
    size_t len = strlen(cur_path);
    if (len > 1 && cur_path[len - 1] == '/') {
        cur_path[len - 1] = '\0';
    }
    char filename[200] ={0};
    strcpy(filename,msg.file_name);
    //printf("msg.filename:%s\n",msg.file_name);
    // char *last_slash = strrchr(cur_path, '/');
    // if (last_slash != NULL) {
    //   strcpy(filename, last_slash + 1);
    //    *last_slash = '\0';
    // }

    int uid = atoi(user->user_id);
   // printf("uid:%d\n",uid);
    bool res = is_file_in_db(uid, path, user, msg,filename);

    recvMsg ret_msg;
    if (res) {
        strcpy(ret_msg.statu, "echo");
        send(net_fd, &ret_msg, sizeof(ret_msg), 0);
        return true;
    }

    bool fast_ret = is_fastputs(uid, path, user, msg, cur_path, filename);
    if (fast_ret) {
        strcpy(ret_msg.statu, "fast");
        send(net_fd, &ret_msg, sizeof(ret_msg), 0);
        return true;
    } else {
        if (!again_send(uid, path, user, msg, filename)) {
            //第一次上传
            insert_column(uid, path, user, msg, cur_path, filename);
            //这里就要在服务端新建文件
        }

        struct stat fileStat;
        long long off;
        if (stat(msg.hash_values, &fileStat) == -1) {
            perror("stat");
            //找不到文件就是偏移量为0
            strcpy(ret_msg.msg,"0");
            //   return false;
        }
        else{
            off = (long long)fileStat.st_size;
            char fileSizeStr[1000];
            sprintf(fileSizeStr, "%lld", off);
            strcpy(ret_msg.msg, fileSizeStr);
        }
        send(net_fd, &ret_msg, sizeof(ret_msg), 0);
      //  printf("发了\n");
        off_t file_size;
        int ret = recv(net_fd, &file_size, sizeof(off_t), MSG_WAITALL);
        if (ret == -1) {
            perror("recv file size");
            return false;
        }

        printf("filesize: %ld\n",file_size);
        char path_root[1100];
        bzero(path_root,sizeof(path_root));
        strcat(path_root,user->root_path);
        strcat(path_root,"/");
        strcat(path_root,msg.hash_values);
        //printf("!!!!!!!!!root_path:%s\n",path_root);
        int file_fd = open(path_root, O_RDWR | O_CREAT, 0600);
        if (file_fd == -1) {
            perror("file_fd");
            return false;
        }

        if (ftruncate(file_fd, file_size) == -1) {
            perror("ftruncate");
            close(file_fd);
            return false;
        }
        
        off_t received_size = off;
        char *p = (char *)mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, file_fd, 0);
        if (p == MAP_FAILED) {
            perror("mmap");
            close(file_fd);
            return false;
        }

        ssize_t ret_recv1;
        while (received_size < file_size) {
            ret_recv1 = recv(net_fd, p + received_size, file_size - received_size, MSG_WAITALL);
            if (ret_recv1 <= 0) {
                if (errno == EINTR || errno == EAGAIN) {
                    continue;
                } else {
                    perror("recv file data");
                    munmap(p, file_size);
                    close(file_fd);
                    return false;
                }
            }
            received_size += ret_recv1;
            sleep(3);
        }

        munmap(p, file_size);
        close(file_fd);
        update_finish(uid, path, user, msg, filename);
    }

    return true;
}
